package com.jlf.core.engine.builder;

import com.jlf.core.engine.constant.SchemaConstant;
import com.jlf.core.engine.domain.SchemaCriterionDO;
import com.jlf.core.engine.domain.SchemaDomainDO;
import com.jlf.core.engine.domain.SchemaFieldDO;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.objectweb.asm.*;

import java.io.File;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 模型领域的管理接口控制器的类构建器
 *
 * @author wujr
 * 2023/7/10
 * @version 1.0
 * 【变更历史】
 * [wujr 2023/7/10 1.0 新增]
 */
@Slf4j
@Getter
public class AutoControllerBuilder {
    private final boolean saveControllerClass = false;
    private Class<?> modalInputClass;
    private Class<?> queryCriteriaClass;
    private Class<?> queryCriteriaNClass;
    private Class<?> queryCriteriaLClass;
    private Class<?> queryCriteriaNLClass;
    private Class<?> queryCriteriaGEClass;
    private Class<?> queryCriteriaGTClass;
    private Class<?> queryCriteriaLEClass;
    private Class<?> queryCriteriaLTClass;
    private Class<?> queryCriteriaLLClass;
    private Class<?> queryCriteriaLRClass;
    private Class<?> queryCriteriaNLLClass;
    private Class<?> queryCriteriaNLRClass;
    private Class<?> controllerClass;
    private String asmControllerInternal;
    private String asmModalInputInternal;
    private String asmCriteriaInternal;
    private String criteriaType;
    private String asmCriteriaNInternal;
    private String asmCriteriaLInternal;
    private String asmCriteriaNLInternal;
    private String asmCriteriaGEInternal;
    private String asmCriteriaGTInternal;
    private String asmCriteriaLEInternal;
    private String asmCriteriaLTInternal;
    private String asmCriteriaLLInternal;
    private String asmCriteriaLRInternal;
    private String asmCriteriaNLLInternal;
    private String asmCriteriaNLRInternal;
    
    private final AutoServerBuilder serverBuilder;
    private final AutoPersistenceBuilder persistenceBuilder;
    private final AutoDomainBuilder domainBuilder;
    private final SchemaDomainDO schemaDomain;
    private final List<SchemaFieldDO> schemaFields;
    private final List<SchemaCriterionDO> schemaCriteria;
    

    public AutoControllerBuilder(AutoServerBuilder serverBuilder,
                                 SchemaDomainDO schemaDomain,
                                 List<SchemaFieldDO> schemaFields,
                                 List<SchemaCriterionDO> schemaCriteria){
        this.serverBuilder = serverBuilder;
        this.schemaDomain = schemaDomain;
        this.schemaFields = schemaFields;
        this.schemaCriteria = schemaCriteria;
        this.persistenceBuilder = serverBuilder.getPersistenceBuilder();
        this.domainBuilder = serverBuilder.getDomainBuilder();
    }
    private Class<?> generateCriteriaInner(String criteriaClass, String innerName, int condition) throws Exception{
        if (CollectionUtils.isEmpty(schemaCriteria)){
            return null;
        }
        boolean hasConditionCriterion = false;
        for (SchemaCriterionDO schemaCriterion : schemaCriteria) {
            if (schemaCriterion.isCriterionSupported(condition)) {
                hasConditionCriterion = true;
                break;
            }
        }
        if (!hasConditionCriterion){
            return null;
        }
        String innerClass = criteriaClass + innerName;
        try{
            return this.domainBuilder.getEngineClassLoader().loadClass(innerClass);
        }catch (Exception e){
            log.trace("加载模型领域：{} 的查询输入条件：{}，该类不存在，需要动态创建", this.domainBuilder.getDomainSimpleCode(), condition);
        }
        String criterionInternal = AutoBuilderTool.class2Asm(criteriaClass);
        String asmInnerType = AutoBuilderTool.class2Asm(innerClass);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, asmInnerType, null, "java/lang/Object", null);
//        cw.visitOuterClass(criterionInternal, null, null);
        cw.visitInnerClass(asmInnerType, criterionInternal, innerName, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);
        // 添加默认构造函数
        AutoBuilderTool.asmDefaultConstructor(cw, asmInnerType, false);
        // 添加查询字段
        for (SchemaCriterionDO schemaCriterion : schemaCriteria) {
            if (schemaCriterion.isCriterionSupported(condition)) {
                AutoBuilderTool.asmAddCriterion(cw, asmInnerType, schemaCriterion);
            }
        }
        cw.visitEnd();
        byte[] code = cw.toByteArray();
        if (saveControllerClass) {
            String tempPath = System.getProperty("java.io.tmpdir") + File.separator;
            FileOutputStream fos = new FileOutputStream(tempPath + this.domainBuilder.getDomainSimpleCode() + "Criteria" + innerName + ".class");
            fos.write(code);
            fos.close();
        }
        return this.domainBuilder.getEngineClassLoader().defineClass(innerClass, code);
    }
    private void visitCriterionInitField(MethodVisitor mv, String criterionInternal, String compareCode) throws Exception{
        // innerClass v = new innerClass();
        Class<?> innerClass = _mapCompareCodeOfInnerClass.get(compareCode);
        if (innerClass == null){
            return;
        }
        String innerInternal = _mapCompareCodeOfInnerInternal.get(compareCode);
        String compareField = getCompareField(compareCode);
        mv.visitTypeInsn(Opcodes.NEW, innerInternal);
        mv.visitInsn(Opcodes.DUP);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, innerInternal, AutoBuilderTool._asmInitName, "()V", false);
        mv.visitVarInsn(Opcodes.ASTORE, 1); // innerClass
        mv.visitVarInsn(Opcodes.ALOAD,0);
        mv.visitVarInsn(Opcodes.ALOAD,1);
        mv.visitFieldInsn(Opcodes.PUTFIELD, criterionInternal, compareField, Type.getDescriptor(innerClass));
    }
    private void visitCriterionConstructor(ClassWriter cw, String criterionClassName) throws Exception{
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, AutoBuilderTool._asmInitName, AutoBuilderTool._asmInitDesc, null,null);
        mv.visitVarInsn(Opcodes.ALOAD,0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, AutoBuilderTool._asmObjectInternal, AutoBuilderTool._asmInitName, AutoBuilderTool._asmInitDesc,false);
        // 初始化变量
        visitCriterionInitField(mv, criterionClassName, "ne");
        visitCriterionInitField(mv, criterionClassName, "like");
        visitCriterionInitField(mv, criterionClassName, "notLike");
        visitCriterionInitField(mv, criterionClassName, "ge");
        visitCriterionInitField(mv, criterionClassName, "gt");
        visitCriterionInitField(mv, criterionClassName, "le");
        visitCriterionInitField(mv, criterionClassName, "lt");
        visitCriterionInitField(mv, criterionClassName, "likeLeft");
        visitCriterionInitField(mv, criterionClassName, "likeRight");
        visitCriterionInitField(mv, criterionClassName, "notLikeLeft");
        visitCriterionInitField(mv, criterionClassName, "notLikeRight");

        mv.visitInsn(Opcodes.RETURN);
        // 更新操作数栈
        mv.visitMaxs(2,2);
        // 一定要有visitEnd
        mv.visitEnd();
    }
    private final Map<String, Class<?>> _mapCompareCodeOfInnerClass = new HashMap<>();
    private final Map<String, String> _mapCompareCodeOfInnerInternal = new HashMap<>();
    public void generateCriteria() throws Exception{
        String criteriaPackage = this.domainBuilder.getBasePackage() + ".entity.criterion";
        String criteriaClass = criteriaPackage + "." + this.domainBuilder.getDomainSimpleCode() + "Criteria";
        this.queryCriteriaNClass = generateCriteriaInner(criteriaClass, "$N", SchemaConstant.Jdbc.Criterion.ne);
        this.queryCriteriaLClass = generateCriteriaInner(criteriaClass, "$L", SchemaConstant.Jdbc.Criterion.like);
        this.queryCriteriaNLClass = generateCriteriaInner(criteriaClass, "$NL", SchemaConstant.Jdbc.Criterion.notLike);
        this.queryCriteriaGTClass = generateCriteriaInner(criteriaClass, "$GT", SchemaConstant.Jdbc.Criterion.gt);
        this.queryCriteriaGEClass = generateCriteriaInner(criteriaClass, "$GE", SchemaConstant.Jdbc.Criterion.ge);
        this.queryCriteriaLTClass = generateCriteriaInner(criteriaClass, "$LT", SchemaConstant.Jdbc.Criterion.lt);
        this.queryCriteriaLEClass = generateCriteriaInner(criteriaClass, "$LE", SchemaConstant.Jdbc.Criterion.le);
        this.queryCriteriaLLClass = generateCriteriaInner(criteriaClass, "$LL", SchemaConstant.Jdbc.Criterion.likeLeft);
        this.queryCriteriaLRClass = generateCriteriaInner(criteriaClass, "$LR", SchemaConstant.Jdbc.Criterion.likeRight);
        this.queryCriteriaNLLClass = generateCriteriaInner(criteriaClass, "$NLL", SchemaConstant.Jdbc.Criterion.notLikeLeft);
        this.queryCriteriaNLRClass = generateCriteriaInner(criteriaClass, "$NLR", SchemaConstant.Jdbc.Criterion.notLikeRight);

        if (this.queryCriteriaNClass != null){
            this.asmCriteriaNInternal = Type.getInternalName(this.queryCriteriaNClass);
            _mapCompareCodeOfInnerClass.put("ne", this.queryCriteriaNClass);
            _mapCompareCodeOfInnerInternal.put("ne", this.asmCriteriaNInternal);
        }
        if (this.queryCriteriaLClass != null){
            this.asmCriteriaLInternal = Type.getInternalName(this.queryCriteriaLClass);
            _mapCompareCodeOfInnerClass.put("like", this.queryCriteriaLClass);
            _mapCompareCodeOfInnerInternal.put("like", this.asmCriteriaLInternal);
        }
        if (this.queryCriteriaNLClass != null){
            this.asmCriteriaNLInternal = Type.getInternalName(this.queryCriteriaNLClass);
            _mapCompareCodeOfInnerClass.put("notLike", this.queryCriteriaNLClass);
            _mapCompareCodeOfInnerInternal.put("notLike", this.asmCriteriaNLInternal);
        }
        if (this.queryCriteriaGEClass != null){
            this.asmCriteriaGEInternal = Type.getInternalName(this.queryCriteriaGEClass);
            _mapCompareCodeOfInnerClass.put("ge", this.queryCriteriaGEClass);
            _mapCompareCodeOfInnerInternal.put("ge", this.asmCriteriaGEInternal);
        }
        if (this.queryCriteriaGTClass != null){
            this.asmCriteriaGTInternal = Type.getInternalName(this.queryCriteriaGTClass);
            _mapCompareCodeOfInnerClass.put("gt", this.queryCriteriaGTClass);
            _mapCompareCodeOfInnerInternal.put("gt", this.asmCriteriaGTInternal);
        }
        if (this.queryCriteriaLEClass != null){
            this.asmCriteriaLEInternal = Type.getInternalName(this.queryCriteriaLEClass);
            _mapCompareCodeOfInnerClass.put("le", this.queryCriteriaLEClass);
            _mapCompareCodeOfInnerInternal.put("le", this.asmCriteriaLEInternal);
        }
        if (this.queryCriteriaLTClass != null){
            this.asmCriteriaLTInternal = Type.getInternalName(this.queryCriteriaLTClass);
            _mapCompareCodeOfInnerClass.put("lt", this.queryCriteriaLTClass);
            _mapCompareCodeOfInnerInternal.put("lt", this.asmCriteriaLTInternal);
        }
        if (this.queryCriteriaLLClass != null){
            this.asmCriteriaLLInternal = Type.getInternalName(this.queryCriteriaLLClass);
            _mapCompareCodeOfInnerClass.put("likeLeft", this.queryCriteriaLLClass);
            _mapCompareCodeOfInnerInternal.put("likeLeft", this.asmCriteriaLLInternal);
        }
        if (this.queryCriteriaLRClass != null){
            this.asmCriteriaLRInternal = Type.getInternalName(this.queryCriteriaLRClass);
            _mapCompareCodeOfInnerClass.put("likeRight", this.queryCriteriaLRClass);
            _mapCompareCodeOfInnerInternal.put("likeRight", this.asmCriteriaLRInternal);
        }
        if (this.queryCriteriaNLLClass != null){
            this.asmCriteriaNLLInternal = Type.getInternalName(this.queryCriteriaNLLClass);
            _mapCompareCodeOfInnerClass.put("notLikeLeft", this.queryCriteriaNLLClass);
            _mapCompareCodeOfInnerInternal.put("notLikeLeft", this.asmCriteriaNLLInternal);
        }
        if (this.queryCriteriaNLRClass != null){
            this.asmCriteriaNLRInternal = Type.getInternalName(this.queryCriteriaNLRClass);
            _mapCompareCodeOfInnerClass.put("notLikeRight", this.queryCriteriaNLRClass);
            _mapCompareCodeOfInnerInternal.put("notLikeRight", this.asmCriteriaNLRInternal);
        }

        try{
            this.queryCriteriaClass = this.domainBuilder.getEngineClassLoader().loadClass(criteriaClass);
            this.asmCriteriaInternal = Type.getInternalName(this.queryCriteriaClass);
            this.criteriaType = Type.getDescriptor(this.queryCriteriaClass);
            return;
        }catch (Exception e){
            log.trace("加载模型领域：{} 的查询输入参数，该类不存在，需要动态创建", this.domainBuilder.getDomainSimpleCode());
        }
        this.asmCriteriaInternal = AutoBuilderTool.class2Asm(criteriaClass);
        this.criteriaType = "L" + this.asmCriteriaInternal + ";";
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
        String[] interfaces = new String[]{AutoBuilderTool._asmQInternal};
        String signature = AutoBuilderTool.asmQSignature(this.domainBuilder.getDomainType());
        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, asmCriteriaInternal, signature, "java/lang/Object", interfaces);
        // 添加内部类
        if (this.queryCriteriaNClass != null) {
            cw.visitInnerClass(asmCriteriaInternal + "$N", asmCriteriaInternal, "N", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);
        }
        if (this.queryCriteriaLClass != null) {
            cw.visitInnerClass(asmCriteriaInternal + "$L", asmCriteriaInternal, "L", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);
        }
        if (this.queryCriteriaNLClass != null) {
            cw.visitInnerClass(asmCriteriaInternal + "$NL", asmCriteriaInternal, "NL", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);
        }
        if (this.queryCriteriaGTClass != null) {
            cw.visitInnerClass(asmCriteriaInternal + "$GT", asmCriteriaInternal, "GT", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);
        }
        if (this.queryCriteriaGEClass != null) {
            cw.visitInnerClass(asmCriteriaInternal + "$GE", asmCriteriaInternal, "GE", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);
        }
        if (this.queryCriteriaLTClass != null) {
            cw.visitInnerClass(asmCriteriaInternal + "$LT", asmCriteriaInternal, "LT", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);
        }
        if (this.queryCriteriaLEClass != null) {
            cw.visitInnerClass(asmCriteriaInternal + "$LE", asmCriteriaInternal, "LE", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);
        }
        if (this.queryCriteriaLLClass != null) {
            cw.visitInnerClass(asmCriteriaInternal + "$LL", asmCriteriaInternal, "LL", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);
        }
        if (this.queryCriteriaLRClass != null) {
            cw.visitInnerClass(asmCriteriaInternal + "$LR", asmCriteriaInternal, "LR", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);
        }
        if (this.queryCriteriaNLLClass != null) {
            cw.visitInnerClass(asmCriteriaInternal + "$NLL", asmCriteriaInternal, "NLL", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);
        }
        if (this.queryCriteriaNLRClass != null) {
            cw.visitInnerClass(asmCriteriaInternal + "$NLR", asmCriteriaInternal, "NLR", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);
        }

        // 添加查询字段
        if (!CollectionUtils.isEmpty(schemaCriteria)) {
            for (SchemaCriterionDO schemaCriterion : schemaCriteria) {
                if (schemaCriterion.isEqCriterion()) {
                    AutoBuilderTool.asmAddCriterion(cw, asmCriteriaInternal, schemaCriterion);
                }
            }
        }
        // 添加内部类变量
        if (this.queryCriteriaNClass != null) {
            AutoBuilderTool.asmAddInnerCriterion(cw, asmCriteriaInternal, this.queryCriteriaNClass, "_e_n");
        }
        if (this.queryCriteriaLClass != null) {
            AutoBuilderTool.asmAddInnerCriterion(cw, asmCriteriaInternal, this.queryCriteriaLClass, "_e_l");
        }
        if (this.queryCriteriaNLClass != null) {
            AutoBuilderTool.asmAddInnerCriterion(cw, asmCriteriaInternal, this.queryCriteriaNLClass, "_e_nl");
        }
        if (this.queryCriteriaGEClass != null) {
            AutoBuilderTool.asmAddInnerCriterion(cw, asmCriteriaInternal, this.queryCriteriaGEClass, "_e_ge");
        }
        if (this.queryCriteriaGTClass != null) {
            AutoBuilderTool.asmAddInnerCriterion(cw, asmCriteriaInternal, this.queryCriteriaGTClass, "_e_gt");
        }
        if (this.queryCriteriaLTClass != null) {
            AutoBuilderTool.asmAddInnerCriterion(cw, asmCriteriaInternal, this.queryCriteriaLTClass, "_e_lt");
        }
        if (this.queryCriteriaLEClass != null) {
            AutoBuilderTool.asmAddInnerCriterion(cw, asmCriteriaInternal, this.queryCriteriaLEClass, "_e_le");
        }
        if (this.queryCriteriaLLClass != null) {
            AutoBuilderTool.asmAddInnerCriterion(cw, asmCriteriaInternal, this.queryCriteriaLLClass, "_e_ll");
        }
        if (this.queryCriteriaLRClass != null) {
            AutoBuilderTool.asmAddInnerCriterion(cw, asmCriteriaInternal, this.queryCriteriaLRClass, "_e_lr");
        }
        if (this.queryCriteriaNLLClass != null) {
            AutoBuilderTool.asmAddInnerCriterion(cw, asmCriteriaInternal, this.queryCriteriaNLLClass, "_e_nll");
        }
        if (this.queryCriteriaNLRClass != null) {
            AutoBuilderTool.asmAddInnerCriterion(cw, asmCriteriaInternal, this.queryCriteriaNLRClass, "_e_nlr");
        }
        // 添加构造函数
        visitCriterionConstructor(cw, asmCriteriaInternal);
        
        Map<String, SchemaFieldDO> mapCodeOfFields = schemaFields.stream().collect(Collectors.toMap(SchemaFieldDO::getClassFieldCode, Function.identity()));
        // 添加内部的多字段or内部静态方法
        if (!CollectionUtils.isEmpty(schemaCriteria)) {
            for (SchemaCriterionDO schemaCriterion : schemaCriteria) {
                String[] classFieldCodes = StringUtils.split(schemaCriterion.getClassFieldCode(), ",");
                if (classFieldCodes.length > 1){
                    Class<?> classDataType = SchemaConstant.Java.type(schemaCriterion.getClassDataType());
                    String asmDataType = Type.getDescriptor(classDataType);
                    if (schemaCriterion.isEqCriterion()) {
                        String lambdaMethod = "lambda$build$eq$" + schemaCriterion.getCriterionCode();
                        visitCriterionBuildLambdaIndex(cw, lambdaMethod,
                                "eq",
                                schemaCriterion.getCriterionCode(), asmDataType,
                                classFieldCodes, mapCodeOfFields);
                    }
                    if (schemaCriterion.isNeCriterion()){
                        String lambdaMethod = "lambda$build$ne$" + schemaCriterion.getCriterionCode();
                        visitCriterionBuildLambdaIndex(cw, lambdaMethod,
                                "ne",
                                schemaCriterion.getCriterionCode(), asmDataType,
                                classFieldCodes, mapCodeOfFields);
                    }
                    if (schemaCriterion.isLikeCriterion()){
                        String lambdaMethod = "lambda$build$l$" + schemaCriterion.getCriterionCode();
                        visitCriterionBuildLambdaIndex(cw, lambdaMethod,
                                "like",
                                schemaCriterion.getCriterionCode(), asmDataType,
                                classFieldCodes, mapCodeOfFields);
                    }
                    if (schemaCriterion.isNotLikeCriterion()){
                        String lambdaMethod = "lambda$build$nl$" + schemaCriterion.getCriterionCode();
                        visitCriterionBuildLambdaIndex(cw, lambdaMethod,
                                "notLike",
                                schemaCriterion.getCriterionCode(), asmDataType,
                                classFieldCodes, mapCodeOfFields);
                    }
                    if (schemaCriterion.isGeCriterion()){
                        String lambdaMethod = "lambda$build$ge$" + schemaCriterion.getCriterionCode();
                        visitCriterionBuildLambdaIndex(cw, lambdaMethod,
                                "ge",
                                schemaCriterion.getCriterionCode(), asmDataType,
                                classFieldCodes, mapCodeOfFields);
                    }
                    if (schemaCriterion.isGtCriterion()){
                        String lambdaMethod = "lambda$build$gt$" + schemaCriterion.getCriterionCode();
                        visitCriterionBuildLambdaIndex(cw, lambdaMethod,
                                "gt",
                                schemaCriterion.getCriterionCode(), asmDataType,
                                classFieldCodes, mapCodeOfFields);
                    }
                    if (schemaCriterion.isLeCriterion()){
                        String lambdaMethod = "lambda$build$le$" + schemaCriterion.getCriterionCode();
                        visitCriterionBuildLambdaIndex(cw, lambdaMethod,
                                "le",
                                schemaCriterion.getCriterionCode(), asmDataType,
                                classFieldCodes, mapCodeOfFields);
                    }
                    if (schemaCriterion.isLtCriterion()){
                        String lambdaMethod = "lambda$build$lt$" + schemaCriterion.getCriterionCode();
                        visitCriterionBuildLambdaIndex(cw, lambdaMethod,
                                "lt",
                                schemaCriterion.getCriterionCode(), asmDataType,
                                classFieldCodes, mapCodeOfFields);
                    }
                    if (schemaCriterion.isLikeLeftCriterion()){
                        String lambdaMethod = "lambda$build$ll$" + schemaCriterion.getCriterionCode();
                        visitCriterionBuildLambdaIndex(cw, lambdaMethod,
                                "likeLeft",
                                schemaCriterion.getCriterionCode(), asmDataType,
                                classFieldCodes, mapCodeOfFields);
                    }
                    if (schemaCriterion.isLikeRightCriterion()){
                        String lambdaMethod = "lambda$build$lr$" + schemaCriterion.getCriterionCode();
                        visitCriterionBuildLambdaIndex(cw, lambdaMethod,
                                "likeRight",
                                schemaCriterion.getCriterionCode(), asmDataType,
                                classFieldCodes, mapCodeOfFields);
                    }
                    if (schemaCriterion.isNotLikeLeftCriterion()){
                        String lambdaMethod = "lambda$build$nll$" + schemaCriterion.getCriterionCode();
                        visitCriterionBuildLambdaIndex(cw, lambdaMethod,
                                "notLikeLeft",
                                schemaCriterion.getCriterionCode(), asmDataType,
                                classFieldCodes, mapCodeOfFields);
                    }
                    if (schemaCriterion.isNotLikeRightCriterion()){
                        String lambdaMethod = "lambda$build$nlr$" + schemaCriterion.getCriterionCode();
                        visitCriterionBuildLambdaIndex(cw, lambdaMethod,
                                "notLikeRight",
                                schemaCriterion.getCriterionCode(), asmDataType,
                                classFieldCodes, mapCodeOfFields);
                    }
                }
            }
        }
        // 重载build
        visitCriterionBuild(cw, mapCodeOfFields);

        cw.visitEnd();
        byte[] code = cw.toByteArray();
        if (saveControllerClass) {
            String tempPath = System.getProperty("java.io.tmpdir") + File.separator;
            FileOutputStream fos = new FileOutputStream(tempPath + this.domainBuilder.getDomainSimpleCode() + "Criteria.class");
            fos.write(code);
            fos.close();
        }
        this.queryCriteriaClass = this.domainBuilder.getEngineClassLoader().defineClass(criteriaClass, code);
    }

    private String getCompareField(String compareCode){
        return "_e_" + _mapCompareCodeOfFields.get(compareCode);
    }
    private void visitCriterionBuildLambdaIndex(ClassWriter cw, String lambdaMethodName,
                                                String compareCode,
                                                String criterionCode, String asmDataType,
                                                String[] classFieldCodes,
                                                Map<String, SchemaFieldDO> mapCodeOfFields) throws Exception{
        // 构建查询的lambda内部方法
        // QueryWrapper<T> lambda$build${index}(QueryWrapper<T>)
        String desc = "(" + AutoBuilderTool._asmQueryWrapperType + ")V";
//        String signature = "(" + AutoBuilderTool._asmQueryWrapperSignature + "<" + this.domainBuilder.getDomainType() + ">;)V";
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC, lambdaMethodName, desc, null, null);
        mv.visitCode();

        mv.visitVarInsn(Opcodes.ALOAD, 0);
        if (!"eq".equalsIgnoreCase(compareCode)){
            Class<?> innerClass = _mapCompareCodeOfInnerClass.get(compareCode);
            String innerClassType = Type.getDescriptor(innerClass);
            String innerClassInternal = _mapCompareCodeOfInnerInternal.get(compareCode);
            String compareField = getCompareField(compareCode);
            mv.visitFieldInsn(Opcodes.GETFIELD, asmCriteriaInternal, compareField, innerClassType);
            String getField = "get" + StringUtils.capitalize(criterionCode);
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, innerClassInternal, getField, "()" + asmDataType, false);
//            mv.visitFieldInsn(Opcodes.GETFIELD, innerClassInternal, criterionCode, asmDataType);
        }else{
            mv.visitFieldInsn(Opcodes.GETFIELD, asmCriteriaInternal, criterionCode, asmDataType);
        }
        mv.visitVarInsn(Opcodes.ASTORE, 2); // 字段的参数值
        boolean first = true;
        for (String classFieldCode : classFieldCodes){
            if (first){
                first = false;
            }else{
                mv.visitVarInsn(Opcodes.ALOAD, 1); // QueryWrapper<T>
                mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, AutoBuilderTool._asmJoinInternal, "or", AutoBuilderTool._asmCompareOrDesc, true);
                mv.visitTypeInsn(Opcodes.CHECKCAST, AutoBuilderTool._asmQueryWrapperInternal);
                mv.visitVarInsn(Opcodes.ASTORE, 1); // QueryWrapper<T>
            }
            SchemaFieldDO schemaField = mapCodeOfFields.get(classFieldCode);
            mv.visitVarInsn(Opcodes.ALOAD, 1); // QueryWrapper<T>
            mv.visitLdcInsn(schemaField.getJdbcFieldCode());
            mv.visitVarInsn(Opcodes.ALOAD, 2);
            mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, AutoBuilderTool._asmCompareInternal, compareCode, AutoBuilderTool._asmCompareDesc, true);
            mv.visitTypeInsn(Opcodes.CHECKCAST, AutoBuilderTool._asmQueryWrapperInternal);
            mv.visitVarInsn(Opcodes.ASTORE, 1); // QueryWrapper<T>
        }

        // 更新操作数栈
        mv.visitMaxs(3,3);
        mv.visitInsn(Opcodes.RETURN);
        mv.visitEnd();
    }

    private Handle metafactory(){
        return new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory",
                AutoBuilderTool._asmLambdaMetafactoryStaticMethodDesc, false);
    }
    static final String acceptDesc = "()" + AutoBuilderTool._asmConsumerType;
    static final String consumerAcceptDesc = "(" + AutoBuilderTool._asmQueryWrapperType + ")V"; // "(Ljava/lang/Object;)V";
    static final Type acceptType = Type.getType("(Ljava/lang/Object;)V");//consumerAcceptDesc);
    static final Type acceptQueryWrapperType = Type.getType(consumerAcceptDesc);
    private void callLambdaAnd(MethodVisitor mv, String lambdaMethodName){
        Handle handleMetafactory = metafactory();
        Handle handle = new Handle(Opcodes.H_INVOKEVIRTUAL, asmCriteriaInternal, lambdaMethodName, consumerAcceptDesc, false);
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        String acceptDesc = "(" + criteriaType + ")" + AutoBuilderTool._asmConsumerType;
        mv.visitInvokeDynamicInsn("accept", acceptDesc, handleMetafactory, acceptType, handle, acceptQueryWrapperType);
        mv.visitVarInsn(Opcodes.ASTORE, 3); // Consumer: lambda$build${index}
        mv.visitVarInsn(Opcodes.ALOAD, 1); // QueryWrapper<T>
        mv.visitVarInsn(Opcodes.ALOAD, 3);// Consumer: lambda$build${index}
        mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, AutoBuilderTool._asmNestedInternal, "and", AutoBuilderTool._asmCompareAndLambdaDesc, true);
//        mv.visitTypeInsn(Opcodes.CHECKCAST, AutoBuilderTool._asmQueryWrapperInternal);
//        mv.visitVarInsn(Opcodes.ASTORE, 1); // QueryWrapper<T>
    }
    private void callCompareField(MethodVisitor mv, SchemaFieldDO schemaField, String compareCode){
        mv.visitVarInsn(Opcodes.ALOAD, 1); // QueryWrapper<T>
        mv.visitLdcInsn(schemaField.getJdbcFieldCode());
        mv.visitVarInsn(Opcodes.ALOAD, 2);
        mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, AutoBuilderTool._asmCompareInternal, compareCode, AutoBuilderTool._asmCompareDesc, true);
//                    mv.visitTypeInsn(Opcodes.CHECKCAST, AutoBuilderTool._asmQueryWrapperInternal);
//                    mv.visitVarInsn(Opcodes.ASTORE, 1); // QueryWrapper<T>
    }

    private void visitCriterionBuild(ClassWriter cw,
                                     Map<String, SchemaFieldDO> mapCodeOfFields) throws Exception{
        // 重载Q.build方法
        // void build(QueryWrapper<T>)
        String desc = "(" + AutoBuilderTool._asmQueryWrapperType + ")V";
        String signature = "(" + AutoBuilderTool._asmQueryWrapperSignature + "<" + this.domainBuilder.getDomainType() + ">;)V";
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "build", desc, signature, null);
        AnnotationVisitor av = mv.visitAnnotation(AutoBuilderTool._asmOverrideType, true);
        av.visitEnd();
        mv.visitCode();

        for (SchemaCriterionDO schemaCriterion : schemaCriteria){
            // 检索条件
            Class<?> classDataType = SchemaConstant.Java.type(schemaCriterion.getClassDataType());
            String asmDataType = Type.getDescriptor(classDataType);
            boolean isStringType = SchemaConstant.Java.isString(schemaCriterion.getClassDataType());
            String[] classFieldCodes = StringUtils.split(schemaCriterion.getClassFieldCode(), ",");
            if (schemaCriterion.isEqCriterion()){
                // 该条件支持eq操作
                mv.visitVarInsn(Opcodes.ALOAD, 0);
                mv.visitFieldInsn(Opcodes.GETFIELD, asmCriteriaInternal, schemaCriterion.getCriterionCode(), asmDataType);
                mv.visitVarInsn(Opcodes.ASTORE, 2);
                mv.visitIntInsn(Opcodes.ALOAD, 2);
                Label endLabel = new Label();
                if (isStringType) {
                    // 字符串
                    // 调用：StringUtils.isBlank
                    mv.visitMethodInsn(Opcodes.INVOKESTATIC, AutoBuilderTool._asmStringUtilsInternal, "isNotBlank", AutoBuilderTool._asmCharSequenceBooleanDesc, false);
                    mv.visitJumpInsn(Opcodes.IFEQ, endLabel);
                } else {
                    mv.visitJumpInsn(Opcodes.IFNULL, endLabel);
                }
                if (classFieldCodes.length == 1){
                    // 单个字段
                    String classFieldCode = classFieldCodes[0];
                    SchemaFieldDO schemaField = mapCodeOfFields.get(classFieldCode);
                    callCompareField(mv, schemaField, "eq");
                }else {
                    // wrapper.and(w -> w.eq(xxx).or().eq(xxx));
                    String lambdaMethodName = "lambda$build$eq$" + schemaCriterion.getCriterionCode();
                    // 调用：wrapper.and(w -> lambda$build${index}(w, value))
                    // 调用内部方法：lambda$build${index}
                    callLambdaAnd(mv, lambdaMethodName);
                }
                mv.visitInsn(Opcodes.POP);
                mv.visitLabel(endLabel);
            }
            if (schemaCriterion.isNeCriterion()) {
                // 该条件支持ne操作
                callNonEqualCriterion(mv, schemaCriterion.getCriterionCode(),
                        isStringType, asmDataType, "ne", classFieldCodes, mapCodeOfFields);
            }
            if (schemaCriterion.isGeCriterion()) {
                // 该条件支持ge操作
                callNonEqualCriterion(mv, schemaCriterion.getCriterionCode(),
                        isStringType, asmDataType, "ge", classFieldCodes, mapCodeOfFields);
            }
            if (schemaCriterion.isGtCriterion()) {
                // 该条件支持gt操作
                callNonEqualCriterion(mv, schemaCriterion.getCriterionCode(),
                        isStringType, asmDataType, "gt", classFieldCodes, mapCodeOfFields);
            }
            if (schemaCriterion.isLeCriterion()) {
                // 该条件支持le操作
                callNonEqualCriterion(mv, schemaCriterion.getCriterionCode(),
                        isStringType, asmDataType, "le", classFieldCodes, mapCodeOfFields);
            }
            if (schemaCriterion.isLtCriterion()) {
                // 该条件支持lt操作
                callNonEqualCriterion(mv, schemaCriterion.getCriterionCode(),
                        isStringType, asmDataType, "lt", classFieldCodes, mapCodeOfFields);
            }
            if (schemaCriterion.isLikeCriterion()) {
                // 该条件支持ge操作
                callNonEqualCriterion(mv, schemaCriterion.getCriterionCode(),
                        isStringType, asmDataType, "like", classFieldCodes, mapCodeOfFields);
            }
            if (schemaCriterion.isNotLikeCriterion()) {
                // 该条件支持ge操作
                callNonEqualCriterion(mv, schemaCriterion.getCriterionCode(),
                        isStringType, asmDataType, "notLike", classFieldCodes, mapCodeOfFields);
            }
            if (schemaCriterion.isLikeLeftCriterion()) {
                // 该条件支持likeLeft操作
                callNonEqualCriterion(mv, schemaCriterion.getCriterionCode(),
                        isStringType, asmDataType, "likeLeft", classFieldCodes, mapCodeOfFields);
            }
            if (schemaCriterion.isLikeRightCriterion()) {
                // 该条件支持likeRight操作
                callNonEqualCriterion(mv, schemaCriterion.getCriterionCode(),
                        isStringType, asmDataType, "likeRight", classFieldCodes, mapCodeOfFields);
            }
            if (schemaCriterion.isNotLikeLeftCriterion()) {
                // 该条件支持notLikeLeft操作
                callNonEqualCriterion(mv, schemaCriterion.getCriterionCode(),
                        isStringType, asmDataType, "notLikeLeft", classFieldCodes, mapCodeOfFields);
            }
            if (schemaCriterion.isNotLikeRightCriterion()) {
                // 该条件支持notLikeRight操作
                callNonEqualCriterion(mv, schemaCriterion.getCriterionCode(),
                        isStringType, asmDataType, "notLikeRight", classFieldCodes, mapCodeOfFields);
            }
        }

        // 更新操作数栈
        mv.visitMaxs(4,0);
        mv.visitInsn(Opcodes.RETURN);
        mv.visitEnd();
    }
    static final Map<String, String> _mapCompareCodeOfFields = new HashMap<>();
    static{
        _mapCompareCodeOfFields.put("ne", "n");
        _mapCompareCodeOfFields.put("like", "l");
        _mapCompareCodeOfFields.put("notLike", "nl");
        _mapCompareCodeOfFields.put("ge", "ge");
        _mapCompareCodeOfFields.put("gt", "gt");
        _mapCompareCodeOfFields.put("le", "le");
        _mapCompareCodeOfFields.put("lt", "lt");
        _mapCompareCodeOfFields.put("likeLeft", "ll");
        _mapCompareCodeOfFields.put("likeRight", "lr");
        _mapCompareCodeOfFields.put("notLikeLeft", "nll");
        _mapCompareCodeOfFields.put("notLikeRight", "nlr");
    }
    private void callNonEqualCriterion(MethodVisitor mv, String criterionCode,
                                       boolean isStringType, String asmDataType,
                                       String compareCode, String[] classFieldCodes,
                                       Map<String, SchemaFieldDO> mapCodeOfFields){
        Class<?> innerClass = _mapCompareCodeOfInnerClass.get(compareCode);
        String innerInternal = _mapCompareCodeOfInnerInternal.get(compareCode);
        String innerClassType = Type.getDescriptor(innerClass);
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        String compareField = getCompareField(compareCode);
        mv.visitFieldInsn(Opcodes.GETFIELD, asmCriteriaInternal, compareField, innerClassType);
        String getField = "get" + StringUtils.capitalize(criterionCode);
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, innerInternal, getField, "()" + asmDataType, false);
        mv.visitVarInsn(Opcodes.ASTORE, 2);
        mv.visitIntInsn(Opcodes.ALOAD, 2);
        Label endLabel = new Label();
        if (isStringType) {
            // 字符串
            // 调用：StringUtils.isBlank
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, AutoBuilderTool._asmStringUtilsInternal, "isNotBlank", AutoBuilderTool._asmCharSequenceBooleanDesc, false);
            mv.visitJumpInsn(Opcodes.IFEQ, endLabel);
        } else {
            mv.visitJumpInsn(Opcodes.IFNULL, endLabel);
        }
        if (classFieldCodes.length == 1){
            String classFieldCode = classFieldCodes[0];
            SchemaFieldDO schemaField = mapCodeOfFields.get(classFieldCode);
            callCompareField(mv, schemaField, compareCode);
        }else {
            // wrapper.and(w -> w.ne(xxx).or().ne(xxx));
            String lambdaMethodName = "lambda$build$" + _mapCompareCodeOfFields.get(compareCode) + "$" + criterionCode;
            // 调用：wrapper.and(w -> lambda$build${index}(w, value))
            // 调用内部方法：lambda$build${index}
            callLambdaAnd(mv, lambdaMethodName);
        }
        mv.visitInsn(Opcodes.POP);
        mv.visitLabel(endLabel);
    }
    public void generateModalInput() throws Exception{
        String inputPackage = this.domainBuilder.getBasePackage() + ".entity.input";
        String modalInputClass = inputPackage + "." + this.domainBuilder.getDomainSimpleCode() + "Input";
        try{
            this.modalInputClass = this.domainBuilder.getEngineClassLoader().loadClass(modalInputClass);
            this.asmModalInputInternal = Type.getInternalName(this.modalInputClass);
            return;
        }catch (Exception e){
            log.trace("加载模型领域：{} 的新增输入参数，该类不存在，需要动态创建", this.domainBuilder.getDomainSimpleCode());
        }
        this.asmModalInputInternal = AutoBuilderTool.class2Asm(modalInputClass);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, asmModalInputInternal, null, "java/lang/Object", null);
        // 添加字段
        for (SchemaFieldDO schemaField : schemaFields){
            addInputSchemaField(cw, asmModalInputInternal, schemaField);
        }
        AutoBuilderTool.asmDefaultConstructor(cw, null, false);
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "to", "()" + this.domainBuilder.getDomainType(), null, null);
        mv.visitCode();
        // A record = new A();
        mv.visitTypeInsn(Opcodes.NEW, this.domainBuilder.getAsmDomainInternal());
        mv.visitInsn(Opcodes.DUP);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, this.domainBuilder.getAsmDomainInternal(), AutoBuilderTool._asmInitName, "()V", false);
        mv.visitVarInsn(Opcodes.ASTORE, 1);
        for (SchemaFieldDO schemaField : schemaFields){
            // 为模型对象的属性赋值
            mv.visitVarInsn(Opcodes.ALOAD, 1);
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            Class<?> classDataType = SchemaConstant.Java.type(schemaField.getClassDataType());
            String asmDataType = Type.getDescriptor(classDataType);
            mv.visitFieldInsn(Opcodes.GETFIELD, asmModalInputInternal, schemaField.getClassFieldCode(), asmDataType);
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, this.domainBuilder.getAsmDomainInternal(), "set" + StringUtils.capitalize(schemaField.getClassFieldCode()), "(" + asmDataType + ")V", false);
        }
        mv.visitVarInsn(Opcodes.ALOAD, 1);
        mv.visitMaxs(2, 2);
        mv.visitInsn(Opcodes.ARETURN);
        mv.visitEnd();
        cw.visitEnd();
        byte[] code = cw.toByteArray();
        if (saveControllerClass) {
            String tempPath = System.getProperty("java.io.tmpdir") + File.separator;
            FileOutputStream fos = new FileOutputStream(tempPath + this.domainBuilder.getDomainSimpleCode() + "Input.class");
            fos.write(code);
            fos.close();
        }
        this.modalInputClass = this.domainBuilder.getEngineClassLoader().defineClass(modalInputClass, code);
    }

    private void addInputSchemaField(ClassWriter cw, String asmInputClass, SchemaFieldDO schemaField) throws Exception{
        Class<?> classDataType = SchemaConstant.Java.type(schemaField.getClassDataType());
        String asmDataType = Type.getDescriptor(classDataType);
        FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE, schemaField.getClassFieldCode(), asmDataType,null,null);
        // 添加注解
        if (Boolean.FALSE.equals(schemaField.getJdbcNullable())){
            // 该字段不能为空
            // 添加注解：@NotNull, @NotBlank
            AnnotationVisitor av;
            if (SchemaConstant.Java.isString(schemaField.getClassDataType())){
                // 字符串
                av = fv.visitAnnotation(AutoBuilderTool._asmNotBlankType, true);
            }else{
                av = fv.visitAnnotation(AutoBuilderTool._asmNotNullType, true);
            }
            av.visit("message", schemaField.getDisplayName() + "不能为空");
            av.visitEnd();
        }
        // 不要忘记end
        fv.visitEnd();
        MethodVisitor mv;
        String methodCode = StringUtils.capitalize(schemaField.getClassFieldCode());
        String getMethod = "get" + methodCode;
        String setMethod = "set" + methodCode;

        mv = cw.visitMethod(Opcodes.ACC_PUBLIC, getMethod, "()" + asmDataType, null, null);
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(Opcodes.GETFIELD, asmInputClass, schemaField.getClassFieldCode(), asmDataType);
        mv.visitInsn(Opcodes.ARETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();
        mv = cw.visitMethod(Opcodes.ACC_PUBLIC, setMethod, "(" + asmDataType + ")V", null, null);
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitVarInsn(Opcodes.ALOAD, 1);
        mv.visitFieldInsn(Opcodes.PUTFIELD, asmInputClass, schemaField.getClassFieldCode(), asmDataType);
        mv.visitMaxs(2, 2);
        mv.visitInsn(Opcodes.RETURN);
        mv.visitEnd();
    }
    public void generate() throws Exception{
        generateModalInput();
        generateCriteria();
        String controllerClass = this.domainBuilder.getBasePackage() + ".controller." + this.domainBuilder.getDomainSimpleCode() + "Controller";
        try{
            this.controllerClass = this.domainBuilder.getEngineClassLoader().loadClass(controllerClass);
            this.asmControllerInternal = Type.getInternalName(this.controllerClass);
            return;
        }catch (Exception e){
            log.trace("加载模型领域：{} 的server，该server不存在，需要动态创建", this.domainBuilder.getDomainSimpleCode());
        }
        this.asmControllerInternal = AutoBuilderTool.class2Asm(controllerClass);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, asmControllerInternal, null, "java/lang/Object", null);
        // 添加注解：@Tag
        AnnotationVisitor av = cw.visitAnnotation(AutoBuilderTool._asmTagType, true);
        av.visit("name", schemaDomain.getDomainName() + "管理接口");
        av.visitEnd();
        // 添加注解：@RestfulController
        av = cw.visitAnnotation(AutoBuilderTool._asmRestfulControllerType, true);
        av.visit("name", schemaDomain.getDomainName() + "管理接口");
        AnnotationVisitor av1 = av.visitArray("path");
        av1.visit(null, schemaDomain.getRestfulPath());
        av1.visitEnd();
//        av.visit("path", schemaDomain.getRestfulPath());
        av.visit("domainClass", Type.getType(this.domainBuilder.getDomainClass()));//asmDomainClass);
        av.visitEnd();
        // 添加业务服务bean
        FieldVisitor fvServer = cw.visitField(Opcodes.ACC_PRIVATE, "_server", this.serverBuilder.getServerType(), null,null);
        // 添加注解：@Autowired
        av = fvServer.visitAnnotation(AutoBuilderTool._asmAutowiredType, true);
        av.visitEnd();
        fvServer.visitEnd();

        // 添加默认构造函数
        AutoBuilderTool.asmDefaultConstructor(cw, asmControllerInternal, false);
        // 添加curd接口
        // 添加add接口
        visitAdd(cw);
        // 添加edit接口
        visitEdit(cw);
        // 添加删除接口
        visitDelete(cw);
        // 添加查询接口
        visitQuery(cw);
        // 添加详情接口
        visitView(cw);

        cw.visitEnd();
        byte[] code = cw.toByteArray();
        if (saveControllerClass) {
            String tempPath = System.getProperty("java.io.tmpdir") + File.separator;
            FileOutputStream fos = new FileOutputStream(tempPath + this.domainBuilder.getDomainSimpleCode() + "Controller.class");
            fos.write(code);
            fos.close();
        }
        this.controllerClass = this.domainBuilder.getEngineClassLoader().defineClass(controllerClass, code);
    }
    private void visitAdd(ClassWriter cw) throws Exception{
        // 添加add接口
        String modalInputType = Type.getDescriptor(modalInputClass);
        String addDesc = "(" + modalInputType + ")" + this.domainBuilder.getDomainType();
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "add", addDesc, null, new String[]{AutoBuilderTool._asmExceptionInternal});
        addRestfulAnnotation(mv, AutoBuilderTool._asmRestfulPostType, "add", "新增", false);
        AnnotationVisitor av = mv.visitParameterAnnotation(0, AutoBuilderTool._asmValidType, true);
        av.visitEnd();
        av = mv.visitParameterAnnotation(0, AutoBuilderTool._asmRequestBodyType, true);
        av.visitEnd();
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 1);
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, asmModalInputInternal, "to", "()" + this.domainBuilder.getDomainType(), false);
        mv.visitVarInsn(Opcodes.ASTORE, 2);
        loadServerField(mv);
        mv.visitVarInsn(Opcodes.ALOAD, 2);
        String serverAddDesc = "(" + AutoBuilderTool._asmSimpleType + ")" + AutoBuilderTool._asmSimpleType;
        String addOwnerServer = AutoBuilderTool._asmSimpleServerInternal;
        mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, addOwnerServer, "add", serverAddDesc, true);
        mv.visitTypeInsn(Opcodes.CHECKCAST, this.domainBuilder.getAsmDomainInternal());
        mv.visitMaxs(3, 3);
        mv.visitInsn(Opcodes.ARETURN);
        mv.visitEnd();
    }
    private void visitView(ClassWriter cw) throws Exception{
        // 添加view接口
        String viewDesc = "(" + AutoBuilderTool._asmSerializableType + ")" + this.domainBuilder.getDomainType();
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "view", viewDesc, null, new String[]{AutoBuilderTool._asmExceptionInternal});
        addRestfulAnnotation(mv, AutoBuilderTool._asmRestfulGetType, "view", "详情", true);
        mv.visitCode();
        String ownerServer = AutoBuilderTool._asmSimpleServerInternal;
        loadServerField(mv);
        mv.visitVarInsn(Opcodes.ALOAD, 1); // recordId
        String serverGetByIdDesc = "(" + AutoBuilderTool._asmSerializableType + ")" + AutoBuilderTool._asmSimpleType;
        mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, ownerServer, "getById", serverGetByIdDesc, true);
        mv.visitTypeInsn(Opcodes.CHECKCAST, this.domainBuilder.getAsmDomainInternal());
        mv.visitVarInsn(Opcodes.ASTORE, 2); // record
        mv.visitVarInsn(Opcodes.ALOAD, 2);
        Label noObjectLabel = new Label();
        Label endLabel = new Label();
        mv.visitJumpInsn(Opcodes.IFNULL, noObjectLabel);
        mv.visitJumpInsn(Opcodes.GOTO, endLabel);

        throwNoObject(mv, noObjectLabel, 3);
        mv.visitLabel(endLabel);
        mv.visitMaxs(4, 4);
        mv.visitVarInsn(Opcodes.ALOAD, 2); // record
        mv.visitInsn(Opcodes.ARETURN);
        mv.visitEnd();
    }

    private void visitQuery(ClassWriter cw) throws Exception{
        String criteriaDesc = Type.getDescriptor(this.queryCriteriaClass);
        String criterionClassDesc = "(" + criteriaDesc + AutoBuilderTool._asmIntegerType + AutoBuilderTool._asmIntegerType + ")" + AutoBuilderTool._asmListWrapperType;
//        String signature = "(" + criteriaDesc + AutoBuilderTool._asmIntegerType + AutoBuilderTool._asmIntegerType + ")" + AutoBuilderTool._asmListWrapperSignature + "<" + this.domainBuilder.getDomainType() + ">";
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "query", criterionClassDesc, null, null);
        addRestfulAnnotation(mv, AutoBuilderTool._asmRestfulGetType, "query", "查询", false);
        AnnotationVisitor av = mv.visitParameterAnnotation(1, AutoBuilderTool._asmRequestParamType, true);
        av.visit("value", "pageNo");
        av.visit("required", false);
        av.visit("defaultValue", "1");
        av.visitEnd();
        av = mv.visitParameterAnnotation(2, AutoBuilderTool._asmRequestParamType, true);
        av.visit("value", "pageSize");
        av.visit("required", false);
        av.visit("defaultValue", "50");
        av.visitEnd();

        mv.visitCode();
        loadServerField(mv);
        String serverQueryDesc = "(" + AutoBuilderTool._asmQType + AutoBuilderTool._asmIntegerType + AutoBuilderTool._asmIntegerType + ")" + AutoBuilderTool._asmListWrapperType;
//        String ownerServer = AutoBuilderTool._asmSimpleServerInternal;
        mv.visitVarInsn(Opcodes.ALOAD, 1);
        mv.visitVarInsn(Opcodes.ALOAD, 2);
        mv.visitVarInsn(Opcodes.ALOAD, 3);
        mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, this.serverBuilder.getAsmServerInternal(), "query", serverQueryDesc, true);
        mv.visitMaxs(4, 4);
        mv.visitInsn(Opcodes.ARETURN);
        mv.visitEnd();
    }
    private void visitDelete(ClassWriter cw) throws Exception{
        String deleteDesc = "(" + AutoBuilderTool._asmSerializableType + ")V";
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "delete", deleteDesc, null, new String[]{AutoBuilderTool._asmExceptionInternal});
        addRestfulAnnotation(mv, AutoBuilderTool._asmRestfulDeleteType, "delete", "删除", true);
        mv.visitCode();
        String ownerServer = AutoBuilderTool._asmSimpleServerInternal;
        loadServerField(mv);
        mv.visitVarInsn(Opcodes.ALOAD, 1); // recordId
        String serverGetByIdDesc = "(" + AutoBuilderTool._asmSerializableType + ")" + AutoBuilderTool._asmSimpleType;
        mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, ownerServer, "getById", serverGetByIdDesc, true);
        mv.visitTypeInsn(Opcodes.CHECKCAST, this.domainBuilder.getAsmDomainInternal());
        mv.visitVarInsn(Opcodes.ASTORE, 3); // record
        mv.visitVarInsn(Opcodes.ALOAD, 3);
        Label labelException = new Label();
        Label labelNormal = new Label();
        mv.visitJumpInsn(Opcodes.IFNULL, labelException);
        mv.visitJumpInsn(Opcodes.GOTO, labelNormal);

        throwNoObject(mv, labelException, 4);

        mv.visitLabel(labelNormal);
        loadServerField(mv);
        mv.visitVarInsn(Opcodes.ALOAD, 3); // record
        String serverDeleteDesc = "(" + AutoBuilderTool._asmSimpleType + ")" + AutoBuilderTool._asmSimpleType;
        mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, ownerServer, "delete", serverDeleteDesc, true);
        mv.visitTypeInsn(Opcodes.CHECKCAST, this.domainBuilder.getAsmDomainInternal());
        mv.visitMaxs(5, 5);
        mv.visitInsn(Opcodes.RETURN);
        mv.visitEnd();
    }
    private void addRestfulAnnotation(MethodVisitor mv, String annotationType, String code, String name, boolean hasPath){
        AnnotationVisitor av = mv.visitAnnotation(annotationType, true);
        av.visit("code", code);
        av.visit("name", name);
        if (hasPath) {
            AnnotationVisitor av1 = av.visitArray("path");
            av1.visit(null, "/{recordId}");
            av1.visitEnd();
        }
        av.visitEnd();
        if (hasPath) {
            // PathVariable参数
            av = mv.visitParameterAnnotation(0, AutoBuilderTool._asmPathVariableType, true);
            av.visit("value", "recordId");
            av.visitEnd();
        }
    }
    private void loadServerField(MethodVisitor mv){
        mv.visitVarInsn(Opcodes.ALOAD, 0); // this
        mv.visitFieldInsn(Opcodes.GETFIELD, asmControllerInternal, "_server", this.serverBuilder.getServerType());
    }
    private void visitEdit(ClassWriter cw) throws Exception{
        // 添加edit接口
        String modalInputType = Type.getDescriptor(modalInputClass);
        String editDesc = "(" + AutoBuilderTool._asmSerializableType + modalInputType + ")" + this.domainBuilder.getDomainType();
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "edit", editDesc, null, new String[]{AutoBuilderTool._asmExceptionInternal});
        addRestfulAnnotation(mv, AutoBuilderTool._asmRestfulPutType, "edit", "编辑", true);
        // input参数
        AnnotationVisitor av = mv.visitParameterAnnotation(1, AutoBuilderTool._asmValidType, true);
        av.visitEnd();
        av = mv.visitParameterAnnotation(1, AutoBuilderTool._asmRequestBodyType, true);
        av.visitEnd();
        Label labelException = new Label();
        Label labelNormal = new Label();
        mv.visitCode();
        String ownerServer = AutoBuilderTool._asmSimpleServerInternal;
        loadServerField(mv);
        mv.visitVarInsn(Opcodes.ALOAD, 1); // recordId
        String serverGetByIdDesc = "(" + AutoBuilderTool._asmSerializableType + ")" + AutoBuilderTool._asmSimpleType;
        mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, ownerServer, "getById", serverGetByIdDesc, true);
        mv.visitTypeInsn(Opcodes.CHECKCAST, this.domainBuilder.getAsmDomainInternal());
        mv.visitVarInsn(Opcodes.ASTORE, 3); // original
        mv.visitVarInsn(Opcodes.ALOAD, 3);
        mv.visitJumpInsn(Opcodes.IFNULL, labelException);
        mv.visitJumpInsn(Opcodes.GOTO, labelNormal);

        throwNoObject(mv, labelException, 4);

        mv.visitLabel(labelNormal);
        mv.visitVarInsn(Opcodes.ALOAD, 2); // input
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, asmModalInputInternal, "to", "()" + this.domainBuilder.getDomainType(), false);
        mv.visitVarInsn(Opcodes.ASTORE, 4); // record
        mv.visitVarInsn(Opcodes.ALOAD, 3); // original
        String idGetSetOwner = this.domainBuilder.getAsmDomainInternal();// AutoBuilderTool._asmSimpleInternal;
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, idGetSetOwner, "getId", AutoBuilderTool._asmGetLongDesc, false);
        mv.visitVarInsn(Opcodes.ASTORE, 5); // id
        mv.visitVarInsn(Opcodes.ALOAD, 4); // record
        mv.visitVarInsn(Opcodes.ALOAD, 5);
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, idGetSetOwner, "setId", AutoBuilderTool._asmSetLongDesc, false);
        if (schemaDomain.isLockOptimistic()){
            // 行版本
            mv.visitVarInsn(Opcodes.ALOAD, 3); // original
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, idGetSetOwner, "getOptimisticVersion", AutoBuilderTool._asmGetIntegerDesc, false);
            mv.visitVarInsn(Opcodes.ASTORE, 5); // OptimisticVersion
            mv.visitVarInsn(Opcodes.ALOAD, 4); // record
            mv.visitVarInsn(Opcodes.ALOAD, 5);
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, idGetSetOwner, "setOptimisticVersion", AutoBuilderTool._asmSetIntegerDesc, false);
        }
        loadServerField(mv);
        mv.visitVarInsn(Opcodes.ALOAD, 3); // original
        mv.visitVarInsn(Opcodes.ALOAD, 4); // record
        String serverEditDesc = "(" + AutoBuilderTool._asmSimpleType + AutoBuilderTool._asmSimpleType + ")" + AutoBuilderTool._asmSimpleType;
        mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, ownerServer, "edit", serverEditDesc, true);
        mv.visitTypeInsn(Opcodes.CHECKCAST, this.domainBuilder.getAsmDomainInternal());
        mv.visitMaxs(6, 6);
        mv.visitInsn(Opcodes.ARETURN);
        mv.visitEnd();
    }
    private void throwNoObject(MethodVisitor mv, Label label, int expIndex){
        mv.visitLabel(label);
        // NoObjectException exception = new NoObjectException(domainClass, domainName);
        mv.visitTypeInsn(Opcodes.NEW, AutoBuilderTool._asmNoObjectExceptionInternal);
        mv.visitInsn(Opcodes.DUP);
        mv.visitLdcInsn(Type.getType(this.domainBuilder.getDomainClass()));
        mv.visitLdcInsn(schemaDomain.getDomainName());
        String expDesc = "(" + Type.getDescriptor(Class.class) + Type.getDescriptor(String.class) + ")V";
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, AutoBuilderTool._asmNoObjectExceptionInternal, AutoBuilderTool._asmInitName, expDesc, false);
        mv.visitVarInsn(Opcodes.ASTORE, expIndex);
        mv.visitVarInsn(Opcodes.ALOAD, expIndex);
        mv.visitInsn(Opcodes.ATHROW);
    }
}
